/******************************************************************************
 * Copyright (C) 2013 ShenZhen ComTop Information Technology Co.,Ltd
 * All Rights Reserved.
 * 本软件为深圳康拓普开发研制。未经本公司正式书面同意，其他任何个人、团体不得使用、
 * 复制、修改或发布本软件.
 *****************************************************************************/

package com.code.common.util;

import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Vector;

/**
 * 日期和时间处理工具类
 * 
 * <pre>
 * 提供与日期、时间相关的工具方法
 * {@link #formatAsString(Date, String)}:根据指定的日期时间格式来获取指定的对象的字符串信息
 * <br>
 * 部分重要改动说明：
 * 1.使用更易理解的方法formatByYMDHMS/formatByYMD/formatByHMS等替换原DateTimeFormatUtils中formatDateTime/formatShortDateTime/formatShortDate等方法
 * 2.使用更易理解的常量FORMAT_YMDHMS/FORMAT_YMD等替换原DateTimeFormatUtils中ISO_DATETIME_FORMAT/ISO_DATE_FORMAT等常量
 * 3.扩展部分方法中的参数类型，现在通常可以直接传入Calendar或Date中的任意一种，而不仅限于Date，或数值型
 * 4.去除部分调整日期的方法中用于判断调整方向的boolean类型参数，分解为更易使用的两个不同方法。
 * </pre>
 * 
 * @author 陈星凌
 * @since TOP4.0
 * @version 2013-1-29 陈星凌 新建，集成原最小系统和TOP中的与时间相关的工具类功能，并优化部分方法。
 */
public final class TimeUtil {
    
    /** 带年月日时分秒的日期时间格式字符串: yyyy-MM-dd HH:mm:ss */
    public final static String FORMAT_YMDHMS = "yyyy-MM-dd HH:mm:ss";
    
    /** 带年月日时分的日期时间格式字符串: yyyy-MM-dd HH:mm */
    public final static String FORMAT_YMDHM = "yyyy-MM-dd HH:mm";
    
    /** 带年月日的日期格式字符串: yyyy-MM-dd */
    public final static String FORMAT_YMD = "yyyy-MM-dd";
    
    /** 带年月的日期格式字符串: yyyy-MM */
    public final static String FORMAT_YM = "yyyy-MM";
    
    /** 带时分秒的时间格式字符串: HH:mm:ss */
    public final static String FORMAT_HMS = "HH:mm:ss";
    
    /**
     * 构造函数
     */
    private TimeUtil() {
    }
    
    /**
     * 根据日期时间格式yyyy-MM-dd HH:mm:ss来获取指定的对象的字符串信息
     * 
     * @param value 需要被转换的日期时间对象引用
     * @return 返回的是格式化后的字符串
     */
    public static String formatByYMDHMS(Date value) {
        return formatAsString(value, FORMAT_YMDHMS);
    }
    
    /**
     * 根据日期时间格式yyyy-MM-dd 来获取指定的对象的字符串信息
     * 
     * @param value 需要被转换的日期时间对象引用
     * @return 返回的是格式化后的字符串
     */
    public static String formatByYMD(Date value) {
        return formatAsString(value, FORMAT_YMD);
    }
    
    /**
     * 根据时间格式HH:mm:ss来获取指定的对象的字符串信息
     * 
     * @param value 需要被转换的日期时间对象引用
     * @return 返回的是格式化后的字符串
     */
    public static String formatByHMS(Date value) {
        return formatAsString(value, FORMAT_HMS);
    }
    
    /**
     * 根据指定的日期时间格式来获取指定的对象的字符串信息
     * 
     * @param value 需要被转换的日期时间对象引用
     * @param format 指定的日期时间格式 为空时使用“年月日时分秒”的形式
     * @return 返回的是格式化后的字符串
     */
    public static String formatAsString(Date value, String format) {
        if (value == null) {
            return "";
        }
        String strFormatStyle = StringUtil.isEmpty(format) ? FORMAT_YMDHMS : format;
        SimpleDateFormat objSimpleDateFormat = new SimpleDateFormat(strFormatStyle);
        return objSimpleDateFormat.format(value);
    }
    
    /**
     * 根据指定的日期时间格式来获取指定的对象的字符串信息
     * 
     * @param format 指定的日期时间格式,为空时将使用默认的日期时间格式yyyy-MM-dd HH:mm:ss
     * @return 返回的是格式化后的字符串
     */
    public static String getCurrentFormatTime(String format) {
        return formatAsString(new Date(), format);
    }
    
    /**
     * 获取指定日期所在的季度。
     * 
     * <pre>
     * DateTimeFormatUtils.getQuarter(java.sql.Date.valueOf(&quot;2004-01-01&quot;)=1
     * DateTimeFormatUtils.getQuarter(java.sql.Date.valueOf(&quot;2004-05-01&quot;)=2
     * DateTimeFormatUtils.getQuarter(java.sql.Date.valueOf(&quot;2004-09-01&quot;)=3
     * DateTimeFormatUtils.getQuarter(java.sql.Date.valueOf(&quot;2004-12-01&quot;)=4
     * DateTimeFormatUtils.getQuarter(null) = 0;
     * </pre>
     * 
     * @param date 需要判断的时间，类型为java.util.Date.
     * @return 返回指定日期所在的季度。
     */
    public static int getQuarter(Date date) {
        if (date == null) {
            return 0;
        }
        Calendar objCalendar = Calendar.getInstance();
        objCalendar.setTime(date);
        int iMonth = objCalendar.get(Calendar.MONTH) + 1;
        if ((iMonth >= 1) && (iMonth <= 3)) {
            return 1;
        } else if ((iMonth >= 4) && (iMonth <= 6)) {
            return 2;
        } else if ((iMonth >= 7) && (iMonth <= 9)) {
            return 3;
        } else {
            return 4;
        }
    }
    
    /**
     * 获取指定日期所在的季度。
     * 
     * <pre>
     * DateTimeFormatUtils.getQuarter(java.sql.Date.valueOf(&quot;2004-01-01&quot;)=1
     * DateTimeFormatUtils.getQuarter(java.sql.Date.valueOf(&quot;2004-05-01&quot;)=2
     * DateTimeFormatUtils.getQuarter(java.sql.Date.valueOf(&quot;2004-09-01&quot;)=3
     * DateTimeFormatUtils.getQuarter(java.sql.Date.valueOf(&quot;2004-12-01&quot;)=4
     * DateTimeFormatUtils.getQuarter(null) = 0;
     * </pre>
     * 
     * @param calendar 需要判断的时间
     * @return 返回指定日期所在的季度。
     */
    public static int getQuarter(Calendar calendar) {
        int iMonth = calendar.get(Calendar.MONTH) + 1;
        if ((iMonth >= 1) && (iMonth <= 3)) {
            return 1;
        } else if ((iMonth >= 4) && (iMonth <= 6)) {
            return 2;
        } else if ((iMonth >= 7) && (iMonth <= 9)) {
            return 3;
        } else {
            return 4;
        }
    }
    
    /**
     * 得到指定日期所在的周。
     * 
     * @param dateValue 需要判断的日期类型。(java.util.Date)
     * @return 得到日期所在的周。
     */
    public static String getWeekID(Date dateValue) {
        String strWeekID = "";
        Calendar objCalendar = Calendar.getInstance();
        objCalendar.setTime(dateValue);
        int iYear = objCalendar.get(Calendar.YEAR);
        int iWeek = objCalendar.get(Calendar.WEEK_OF_YEAR);
        if (iWeek >= 10) {
            strWeekID = iYear + String.valueOf(iWeek);
        } else {
            strWeekID = iYear + "0" + iWeek;
        }
        return strWeekID;
    }
    
    /**
     * 得到指定日期所在的周。
     * 
     * @param calendar 需要判断的日期
     * @return 得到日期所在的周。
     */
    public static String getWeekID(Calendar calendar) {
        String strWeekID = "";
        int iYear = calendar.get(Calendar.YEAR);
        int iWeek = calendar.get(Calendar.WEEK_OF_YEAR);
        if (iWeek >= 10) {
            strWeekID = iYear + String.valueOf(iWeek);
        } else {
            strWeekID = iYear + "0" + iWeek;
        }
        return strWeekID;
    }
    
    /**
     * 得到所在周的第一天
     * 
     * @param year 需要判断的年份。
     * @param week 需要判断的第几周。
     * @return 得到指定年份，指定周的第一天。
     */
    public static String firstDayOfWeek(int year, int week) {
        Calendar objCalendar = Calendar.getInstance();
        objCalendar.set(Calendar.YEAR, year);
        objCalendar.set(Calendar.WEEK_OF_YEAR, week);
        // 考虑Calendar.setFirstDayOfWeek()
        objCalendar.add(Calendar.DAY_OF_WEEK, (-1 * objCalendar.get(Calendar.DAY_OF_WEEK)) + 2);
        return formatByYMD(objCalendar.getTime());
    }
    
    /**
     * 得到所在周的第一天
     * 
     * @param calendar 指定日期
     * @return 得到指定年份，指定周的第一天。
     */
    public static String firstDayOfWeek(Calendar calendar) {
        // 考虑Calendar.setFirstDayOfWeek()
        calendar.add(Calendar.DAY_OF_WEEK, (-1 * calendar.get(Calendar.DAY_OF_WEEK)) + 2);
        return formatByYMD(calendar.getTime());
    }
    
    /**
     * 得到所在周的最后一天
     * 
     * @param year 需要判断的年份。
     * @param week 需要判断的第几周。
     * @return 得到指定年份，指定周的最后一天。
     */
    public static String endDayOfWeek(int year, int week) {
        Calendar objCalendar = Calendar.getInstance();
        objCalendar.set(Calendar.YEAR, year);
        objCalendar.set(Calendar.WEEK_OF_YEAR, week);
        // 考虑Calendar.setFirstDayOfWeek()
        objCalendar.add(Calendar.DAY_OF_WEEK, (-1 * objCalendar.get(Calendar.DAY_OF_WEEK)) + 2 + 6);
        return formatByYMD(objCalendar.getTime());
    }
    
    /**
     * 得到所在周的最后一天
     * 
     * @param calendar 指定日期
     * @return 得到指定年份，指定周的最后一天。
     */
    public static String endDayOfWeek(Calendar calendar) {
        // 考虑Calendar.setFirstDayOfWeek()
        calendar.add(Calendar.DAY_OF_WEEK, (-1 * calendar.get(Calendar.DAY_OF_WEEK)) + 2 + 6);
        return formatByYMD(calendar.getTime());
    }
    
    /**
     * 得到指定日期所在周的前一周
     * 
     * @param year 需要判断的年份。
     * @param week 需要判断的第几周。
     * @return 得到指定日期所在周的前一周或是下一周
     */
    public static String lastWeekOfDate(int year, int week) {
        String strWeekID = "";
        Calendar objCalendar = Calendar.getInstance();
        objCalendar.set(Calendar.YEAR, year);
        objCalendar.set(Calendar.WEEK_OF_YEAR, week);
        objCalendar.add(Calendar.DAY_OF_WEEK, 7);
        int iYear = objCalendar.get(Calendar.YEAR);
        int iWeek = objCalendar.get(Calendar.WEEK_OF_YEAR);
        if (iWeek < 10) {
            strWeekID = iYear + "0" + iWeek;
        } else {
            strWeekID = iYear + String.valueOf(iWeek);
        }
        return strWeekID;
    }
    
    /**
     * 得到指定日期所在周的前一周
     * 
     * @param calendar 指定日期
     * @return 得到指定日期所在周的前一周或是下一周
     */
    public static String lastWeekOfDate(Calendar calendar) {
        String strWeekID = "";
        int iYear = calendar.get(Calendar.YEAR);
        int iWeek = calendar.get(Calendar.WEEK_OF_YEAR);
        if (iWeek < 10) {
            strWeekID = iYear + "0" + iWeek;
        } else {
            strWeekID = iYear + String.valueOf(iWeek);
        }
        return strWeekID;
    }
    
    /**
     * 得到指定日期所在周的前一周或是下一周
     * 
     * @param year 需要判断的年份。
     * @param week 需要判断的第几周。
     * @return 得到指定日期所在周的前一周或是下一周
     */
    public static String nextWeekOfDate(int year, int week) {
        String strWeekID = "";
        Calendar objCalendar = Calendar.getInstance();
        objCalendar.set(Calendar.YEAR, year);
        objCalendar.set(Calendar.WEEK_OF_YEAR, week);
        objCalendar.add(Calendar.DAY_OF_WEEK, -7);
        int iYear = objCalendar.get(Calendar.YEAR);
        int iWeek = objCalendar.get(Calendar.WEEK_OF_YEAR);
        if (iWeek < 10) {
            strWeekID = iYear + "0" + iWeek;
        } else {
            strWeekID = iYear + String.valueOf(iWeek);
        }
        return strWeekID;
    }
    
    /**
     * 得到指定日期所在周的前一周或是下一周
     * 
     * @param calendar 指定日期
     * @return 得到指定日期所在周的前一周或是下一周
     */
    public static String nextWeekOfDate(Calendar calendar) {
        String strWeekID = "";
        int iYear = calendar.get(Calendar.YEAR);
        int iWeek = calendar.get(Calendar.WEEK_OF_YEAR);
        if (iWeek < 10) {
            strWeekID = iYear + "0" + iWeek;
        } else {
            strWeekID = iYear + String.valueOf(iWeek);
        }
        return strWeekID;
    }
    
    /**
     * 返回当前周除指定日期以外的日期集合.
     * 
     * @param year 需要判断的年份。
     * @param week 需要判断的第几周。
     * @param deviateDate 需要被排除的日期字符串集合
     * @return 返回当前周除指定日期以外的日期集合。
     */
    public static String[] otherDayOfWeek(int year, int week, String[] deviateDate) {
        List<String> objDates = new Vector<String>();
        Calendar objCalendar = Calendar.getInstance();
        objCalendar.set(Calendar.YEAR, year);
        objCalendar.set(Calendar.WEEK_OF_YEAR, week);
        for (int i = 0; i < 7; i++) {
            objCalendar.add(Calendar.DAY_OF_WEEK, (-1 * objCalendar.get(Calendar.DAY_OF_WEEK)) + 2 + i);
            objDates.add(formatByYMD(objCalendar.getTime()));
        }
        if (deviateDate != null) {
            for (int j = 0; j < deviateDate.length; j++) {
                if (objDates.contains(deviateDate[j])) {
                    objDates.remove(deviateDate[j]);
                }
            }
        }
        String[] strRtn = new String[objDates.size()];
        for (int k = 0; k < objDates.size(); k++) {
            strRtn[k] = objDates.get(k);
        }
        return strRtn;
    }
    
    /** 年 */
    public final static int YEAR = 1;
    
    /** 月 */
    public final static int MONTH = 2;
    
    /** 日 */
    public final static int DAY = 3;
    
    /** 时 */
    public final static int HOUR = 4;
    
    /** 分 */
    public final static int MINUTE = 5;
    
    /** 秒 */
    public final static int SECOND = 6;
    
    /** 周 */
    public final static int WEEK = 7;
    
    /** 毫秒 */
    public final static int MILLISECOND = 8;
    
    /** 世纪 */
    public final static int CENTURY = 9;
    
    /** 星期日 */
    public final static int SUNDAY = Calendar.SUNDAY;
    
    /** 星期一 */
    public final static int MONDAY = Calendar.MONDAY;
    
    /** 星期三 */
    public final static int WEDNESDAY = Calendar.WEDNESDAY;
    
    /** 星期二 */
    public final static int TUESDAY = Calendar.TUESDAY;
    
    /** 星期四 */
    public final static int THURSDAY = Calendar.THURSDAY;
    
    /** 星期五 */
    public final static int FRIDAY = Calendar.FRIDAY;
    
    /** 星期六 */
    public final static int SATURDAY = Calendar.SATURDAY;
    
    /**
     * <pre>
     * 获取若干偏移量的时间戳(Timestamp)
     * </pre>
     * 
     * @param operTime 操作时间戳(Timestamp)
     * @param unit 偏移单位
     * @param value 偏移值
     * @return 偏移后的时间戳(Timestamp)
     */
    public static Timestamp getTimestamp(Timestamp operTime, int unit, int value) {
        if (operTime == null) {
            return null;
        }
        Calendar c = Calendar.getInstance();
        c.setTime(operTime);
        Timestamp newTime = null;
        switch (unit) {
            case YEAR:
                c.set(Calendar.YEAR, c.get(Calendar.YEAR) + value);
                newTime = new Timestamp(c.getTimeInMillis());
                break;
            case MONTH:
                c.set(Calendar.MONTH, c.get(Calendar.MONTH) + value);
                newTime = new Timestamp(c.getTimeInMillis());
                break;
            case DAY:
                c.set(Calendar.DATE, c.get(Calendar.DATE) + value);
                newTime = new Timestamp(c.getTimeInMillis());
                break;
            case HOUR:
                c.set(Calendar.HOUR, c.get(Calendar.HOUR) + value);
                newTime = new Timestamp(c.getTimeInMillis());
                break;
            case MINUTE:
                c.set(Calendar.MINUTE, c.get(Calendar.MINUTE) + value);
                newTime = new Timestamp(c.getTimeInMillis());
                break;
            case SECOND:
                c.set(Calendar.SECOND, c.get(Calendar.SECOND) + value);
                newTime = new Timestamp(c.getTimeInMillis());
                break;
            case WEEK:
                c.set(Calendar.DATE, c.get(Calendar.DATE) + 7 * value);
                newTime = new Timestamp(c.getTimeInMillis());
                break;
            case MILLISECOND:
                c.set(Calendar.MILLISECOND, c.get(Calendar.MILLISECOND) + value);
                newTime = new Timestamp(c.getTimeInMillis());
                break;
            case CENTURY:
                c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 100 * value);
                newTime = new Timestamp(c.getTimeInMillis());
                break;
            default:
                break;
        }
        return newTime;
    }
    
    //
    /**
     * <pre>
     * 获取精度到指定单位的时间戳字符串
     * </pre>
     * 
     * @param operTime 时间戳
     * @param unit 时间单位
     * @return 指定单位的时间戳字符串
     */
    public static String getTimestampString(Timestamp operTime, int unit) {
        if (operTime != null) {
            String str = "N/A";
            switch (unit) {
                case YEAR:
                    str = operTime.toString().substring(0, 4);
                    break;
                case MONTH:
                    str = operTime.toString().substring(0, 7);
                    break;
                case DAY:
                    str = operTime.toString().substring(0, 10);
                    break;
                case HOUR:
                    str = operTime.toString().substring(0, 13);
                    break;
                case MINUTE:
                    str = operTime.toString().substring(0, 16);
                    break;
                case SECOND:
                    str = operTime.toString().substring(0, 19);
                    break;
                case MILLISECOND:
                    str = operTime.toString();
                    break;
                default:
                    break;
            }
            return str;
        }
        return "N/A";
    }
    
    /**
     * <pre>
     * 判断两个时间戳(Timestamp)是否在同一天
     * </pre>
     * 
     * @param time1 Timestamp1
     * @param time2 Timestamp2
     * @return 是否在同一天
     */
    public static boolean isSameDay(Timestamp time1, Timestamp time2) {
        if (time1 != null && time2 != null) {
            Calendar c1 = Calendar.getInstance();
            c1.setTime(time1);
            int y1 = c1.get(Calendar.YEAR);
            int m1 = c1.get(Calendar.MONTH);
            int d1 = c1.get(Calendar.DATE);
            Calendar c2 = Calendar.getInstance();
            c2.setTime(time2);
            int y2 = c2.get(Calendar.YEAR);
            int m2 = c2.get(Calendar.MONTH);
            int d2 = c2.get(Calendar.DATE);
            if (y1 == y2 && m1 == m2 && d1 == d2) {
                return true;
            }
        } else {
            if (time1 == null && time2 == null) {
                return true;
            }
        }
        return false;
    }
    
}